// Copyright (c) Microsoft Corporation
//
// All rights reserved. 
//
// Licensed under the Apache License, Version 2.0 (the ""License""); you may not use this file 
// except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING 
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY 
// OR NON-INFRINGEMENT. 
//
// See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using TED.WindowsAzure.Unplugged.Mobile.Exceptions;
using TED.WindowsAzure.Unplugged.Mobile.Interfaces;
using TED.WindowsAzure.Unplugged.Mobile.Sync.Events;
using TED.WindowsAzure.Unplugged.Mobile.Sync.File;
using TED.WindowsAzure.Unplugged.Mobile.Sync.Folder;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI.Xaml.Controls;

namespace DPE.TED.OCAF.Win8SampleClient.ViewModels
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        private ISyncFolder _folder;

        private IUnpluggedMobileServiceClient _client = App.MobileClient;

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel(INavigationService navSvc)
        {
            this.NavigationService = navSvc;

            this.SyncCommand = new RelayCommand(Sync);
            this.InsertCommand = new RelayCommand(Insert);
            this.DeleteCommand = new RelayCommand(Delete);
            this.RefreshCommand = new RelayCommand(Refresh);
            this.SwitchFolderCommand = new RelayCommand(SwitchFolder);

            // Need a way to remove the XAML dependency from here
            //this.ViewCommand = new RelayCommand<PdfViewModel>(ViewPdf);
            this.ViewCommand = new RelayCommand<ItemClickEventArgs>(ViewPdf);
            this.SelectedFolder = "documents";
            this.FolderList = new ObservableCollection<string>();
            this.FolderList.Insert(0, "lotsofpdfdocs");
            this.FolderList.Insert(0, "documents");
            this.ConnectionStatus = "Offline";
            this.SyncProgressIndeterminate = true;
            this.SyncProgressVisibility = 0.0D; // or Visible
            BindList();
        }

        public string AppName
        {
            get { return "All my PDFs"; }
        }
        
        private string _ConnectionStatus = default( string );

        public string ConnectionStatus
        {
            get
            {
                return _ConnectionStatus;
            }

            set
            {
                _ConnectionStatus = value;
                RaisePropertyChanged( "ConnectionStatus" );
            }
        }

        private ICommand _viewCommand = default(ICommand);

        public ICommand ViewCommand
        {
            get
            {
                return _viewCommand;
            }

            set
            {
                _viewCommand = value;
                RaisePropertyChanged();
            }
        }

        private ICommand _refreshCommand = default(ICommand);

        public ICommand RefreshCommand
        {
            get
            {
                return _refreshCommand;
            }

            set
            {
                _refreshCommand = value;
                RaisePropertyChanged();
            }
        }

        private ICommand _switchFolderCommand = default(ICommand);

        public ICommand SwitchFolderCommand
        {
            get
            {
                return _switchFolderCommand;
            }

            set
            {
                _switchFolderCommand = value;
                RaisePropertyChanged();
            }
        }

        private ICommand _insertCommand = default(ICommand);

        public ICommand InsertCommand
        {
            get
            {
                return _insertCommand;
            }

            set
            {
                _insertCommand = value;
                RaisePropertyChanged();
            }
        }

        private double _syncProgressValue = default(double);

        public double SyncProgressValue
        {
            get
            {
                return _syncProgressValue;
            }

            set
            {
                _syncProgressValue = value;
                RaisePropertyChanged();
            }
        }

        private double _SyncProgressVisibility = default( double );

        public double SyncProgressVisibility
        {
            get
            {
                return _SyncProgressVisibility;
            }

            set
            {
                _SyncProgressVisibility = value;
                RaisePropertyChanged( "SyncProgressVisibility" );
            }
        }
        
        private bool _SyncProgressIndeterminate = default( bool );

        public bool SyncProgressIndeterminate
        {
            get
            {
                return _SyncProgressIndeterminate;
            }

            set
            {
                _SyncProgressIndeterminate = value;
                RaisePropertyChanged( "SyncProgressIndeterminate" );
            }
        }

        private ICommand _deleteCommand = default(ICommand);

        public ICommand DeleteCommand
        {
            get
            {
                return _deleteCommand;
            }

            set
            {
                _deleteCommand = value;
                RaisePropertyChanged();
            }
        }

        private ICommand _syncCommand = default(ICommand);

        public ICommand SyncCommand
        {
            get
            {
                return _syncCommand;
            }

            set
            {
                _syncCommand = value;
                RaisePropertyChanged();
            }
        }

        private async Task BindList()
        {
            this._folder = await _client.SyncContext.GetSyncFolderAsync(SelectedFolder);
            var localFiles = await _folder.ReadAsync();

            var pdf = from p in localFiles
                       where p.Status != SyncFileStatus.Deleted && p.Status != SyncFileStatus.Undefined
                       select new PdfViewModel
                       {
                           FileName = p.FileName,
                           SyncFile = p
                       };

            this.PdfList = ConvertToObservable(pdf);
        }

        private string scrubName(string fileName)
        {
            char[] badchars = "()&^%$#@!~`'[]{}._+= ".ToCharArray();

            // all lower case
            fileName = fileName.ToLower();

            // change space and non alphanumeric to dashes
            int foundindex = 0;
            do
            {
                foundindex = fileName.IndexOfAny(badchars, foundindex);
                if (foundindex >= 0)
                {
                    fileName = fileName.Remove(foundindex, 1);
                }
            } while (foundindex >= 0);

            return fileName;
        }

        private async void SwitchFolder()
        {
            if (SelectedFolder != _folder.FolderName)
            {
                this.PdfList.Clear();
                BindList();
            }
        }

        private async void Refresh()
        {
            await BindList();
        }

        private async void Sync()
        {
            try
            {
                Debug.WriteLine("Syncing Documents");

                // pull the pdfs
                _folder.PullStarted += _folder_PullStarted;
                _folder.PushStarted +=_folder_PushStarted;
                _folder.PushCompleted += _folder_PushCompleted;
                _folder.PullCompleted += _folder_PullCompleted;
                _folder.SyncListBuilt += folder_SyncListBuilt;
                _folder.PushProgressChanged += folder_progressChanged;
                _folder.PullProgressChanged += folder_progressChanged;
                _folder.SyncConnection += folder_SyncConnection;

                // push the pdfs
                await this._folder.PullAsync();
                await this._folder.PushAsync();

                _folder.SyncListBuilt -= folder_SyncListBuilt;
                _folder.PushProgressChanged -= folder_progressChanged;
                _folder.PullProgressChanged -= folder_progressChanged;
                _folder.PullStarted -= _folder_PullStarted;
                _folder.PushStarted -= _folder_PushStarted;
                _folder.PushCompleted -= _folder_PushCompleted;
                _folder.PullCompleted -= _folder_PullCompleted;
            }
            catch (Exception ex) 
            {
                Debug.WriteLine( "Exception caught: " + ex.Message );
            }

            await BindList();

            // Sync local comments (for now, only ones that already exist locally)
            foreach (KeyValuePair<string, ISyncFolder> commentFolder in App.CommentsFolders)
            {
                await commentFolder.Value.PushAsync();
                await commentFolder.Value.PullAsync();
            }
        }

        private void _folder_PullStarted( object sender, SyncStartedEventArgs e )
        {
            ConnectionStatus = "Pull Started";
            SyncProgressIndeterminate = true;
            SyncProgressVisibility = 1.0D;
        }

        private void _folder_PushStarted( object sender, SyncStartedEventArgs e )
        {
            ConnectionStatus = "Push Started";
            SyncProgressIndeterminate = true;
            SyncProgressVisibility = 1.0D;
        }

        private void _folder_PullCompleted( object sender, SyncCompletedEventArgs e )
        {
            ConnectionStatus = "";
            SyncProgressIndeterminate = true;
            SyncProgressVisibility = 0.0D;
        }

        private void _folder_PushCompleted( object sender, SyncCompletedEventArgs e )
        {
            ConnectionStatus = "";
            SyncProgressIndeterminate = true;
            SyncProgressVisibility = 0.0D;
        }

        private void folder_SyncConnection( object sender, SyncConnectionEventArgs e )
        {
            Debug.WriteLine( "Exception caught: " + e.Message );

            // Set Text if offline
            ConnectionStatus = "Sync Error";
        }

        private void folder_SyncListBuilt(object sender, SyncListBuiltEventArgs e)
        {
            _totalFileCount = e.SyncFiles.Count();
            SyncProgressIndeterminate = false;
            ConnectionStatus = "Getting File List";
        }

        private async void folder_progressChanged(object sender, SyncProgressEventArgs e)
        {
            ConnectionStatus = "Online";

            // Update progress
            _progressFileCount++;
            this.SyncProgressValue = 100.0 / _totalFileCount * _progressFileCount;

            // Check for errors
            await BindList();
        }

        /// <summary>
        /// Navigates to the correct View Model Page. This method should really use PdfViewModel instead of ItemClickEventArgs
        /// </summary>
        /// <param name="vm"></param>
        private void ViewPdf(ItemClickEventArgs vm)
        {
            this.NavigationService.Navigate(typeof(RenderPage), vm.ClickedItem);
        }

        private async void Insert()
        {
            FileOpenPicker openPicker = new FileOpenPicker();
            openPicker.ViewMode = PickerViewMode.Thumbnail;
            openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
            openPicker.FileTypeFilter.Add(".pdf");
            StorageFile media = await openPicker.PickSingleFileAsync();
            StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder;

            if (media != null)
            {
                try
                {
                    var file = await _folder.CreateFileAsync(media.Name);
                    await _folder.InsertAndCommitAsync(file, await media.OpenAsync(FileAccessMode.ReadWrite));
                }
                catch (SyncException)
                {
                    // need to display a message to tell the client a file already exists
                }

                await BindList();
            }
        }

        private async void Delete()
        {
            if (this.SelectedPdf != null)
            {
                try
                {
                    var file = await _folder.LookupAsync(this.SelectedPdf.FileName);

                    await _folder.DeleteAsync(file);
                }
                catch 
                { 
                }

                await BindList();
            }
        }

        #region Properties

        private ObservableCollection<PdfViewModel> _PdfList = default(ObservableCollection<PdfViewModel>);

        public ObservableCollection<PdfViewModel> PdfList
        {
            get
            {
                return _PdfList;
            }

            set
            {
                _PdfList = value;
                RaisePropertyChanged("PdfList");
            }
        }

        private string _SelectedFolder = default(string);

        public string SelectedFolder
        {
            get
            {
                return _SelectedFolder;
            }

            set
            {
                _SelectedFolder = value;

                RaisePropertyChanged("SelectedFolder");
            }
        }

        private ObservableCollection<string> _FolderList = default(ObservableCollection<string>);

        public ObservableCollection<string> FolderList
        {
            get
            {
                return _FolderList;
            }

            set
            {
                _FolderList = value;
                RaisePropertyChanged("FolderList");
            }
        }

        private PdfViewModel _SelectedPdf = default(PdfViewModel);

        public PdfViewModel SelectedPdf
        {
            get
            {
                return _SelectedPdf;
            }

            set
            {
                _SelectedPdf = value;
                RaisePropertyChanged("SelectedPdf");
            }
        }

        private string _UserName = default(string);

        public string UserName
        {
            get
            {
                return _UserName;
            }

            set
            {
                _UserName = value;
                RaisePropertyChanged("UserName");
            }
        }

        #endregion

        internal INavigationService NavigationService { get; set; }

        private ObservableCollection<PdfViewModel> ConvertToObservable(IEnumerable<PdfViewModel> list)
        {
            var observableList = new ObservableCollection<PdfViewModel>();

            foreach (var b in list)
                observableList.Add(b);

            return observableList;
        }

        private double _totalFileCount;
        private double _progressFileCount;
    }
}